// Michael Depuy
// depuym@onid.orst.edu
// CS344-400
// Homework 5

#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <math.h>
#include <pthread.h>
#include <limits.h>
#include <stdint.h>
#include <getopt.h>

#define UINT_T unsigned int
#define MAX_INT 4294967296

// These macros retreieved from "www.mathcs.emory.edu"
#define SetBit(A,k)        ( A[(k/32)] |= (1 << (k%32)) )
#define ClearBit(A,k)      ( A[(k/32)] &= ~(1 << (k%32)) )
#define TestBit(A,k)       ( A[(k/32)] & (1 << (k%32)) )
pthread_mutex_t *mutex_primes;


// THIS PROGRAM WORKS WITH MULTIPLE THREADS
// USING THE PTHREADS LIBRARY

// Purely for PThread purposes
struct threaded_primes{
  UINT_T *primes_array;
  int beg;
  int end;
} PrimeStruct;


/* Helper function to print the value of a certain bit
   Code borrowed from TestBit() */


void PrintBit(UINT_T A[ ], int k){
  int i = k/32;
  int pos = k%32;

  UINT_T flag = 1;

  flag = flag << pos;

  if (A[i] & flag)
    printf("%d",1);
  else
    printf("%d",0);
}

/* Create list of primes */

void *createPrimeList(void *arg){

  int i,j;
  struct threaded_primes *t = (struct threaded_primes*)arg;
  printf("Thread %d is in createPrimeList()\n",pthread_self());

  // Mark non-prime bits with 1
  // Upper limit is sqrt(n) because the algorithm this uses starts marking non-primes
  // at the square of the prime. This also aleviates any Out of Memory errors,
  // or so I've found.
  for(i=t->beg; i<t->end; i++){
    if(TestBit(t->primes_array,i) != 1){
      for(j=i*i; j<t->end; j++){
	SetBit(t->primes_array,j);
      }
    }
  }
  memcpy(mutex_primes,t->primes_array,sizeof(t->primes_array));
  return (void *)t;

}

// Find a pair of primes who difference is 2
void *twinPrime(void *arg){

  
  
  return 0;
}



void main(int argc, char **argv){
  
  int i,j; // Loop counters
  int opt;
  int prime_size = 10000;
  int num_threads = 1;
  int bit = 0;
  int print = 1;
  int num_twin_primes = 0; // Used for evaluation during quiet phase 

  while((opt = getopt(argc,argv,"qm:c:")) != -1){
    switch(opt){

    case 'q':
      print = 0;
      break;

    case 'm':
      prime_size = atoi(optarg);
      if(prime_size > MAX_INT){
	printf("-m argument too large. Resetting to UMAX_INT.\n");
	prime_size = MAX_INT-1;
      }
      break;

    case 'c':
      num_threads = atoi(optarg);
      break;

    default:
      break;
    }
  }
  /* Create threads and populate */
  pthread_t *threads = (pthread_t *)calloc(num_threads,sizeof(pthread_t));

  pthread_attr_t attr;
  pthread_attr_init(&attr);

  UINT_T array_size = (prime_size/32);
  UINT_T *primes = (UINT_T *)calloc(array_size,sizeof(UINT_T));
  struct threaded_primes tp[num_threads];
  int frac = (int)(prime_size/num_threads);
  printf("frac = %d\n",frac);
  
  for(i=0; i<num_threads; i++){
    tp[i].primes_array = primes;
    printf("%d\t",i);
    if(i == 0){
      tp[i].beg = 2;
      tp[i].end = tp[i].beg + frac;
    }

    else if((i+1) == num_threads){
      tp[i].beg = tp[i-1].end + 1;
      tp[i].end = prime_size;
    }
    else{
      tp[i].beg = tp[i-1].end + 1;
      tp[i].end = tp[i].beg + frac;
    }
    pthread_create(&threads[i],&attr,createPrimeList,&tp[i]);
    printf("Thread %d created\n",i);
  }

  /* Destroy threads */
  for(i=0; i<num_threads; i++){
    pthread_join(threads[i],NULL);
    printf("Thread %d destroyed...\n",i);
  }

  int fwd_bit;

  if(print = 1){
    for(bit=2; bit<=prime_size; bit++){
      fwd_bit = bit + 2;
      if((TestBit(primes,bit) == 0) && (TestBit(primes,fwd_bit) == 0)){
	  printf("(%d, %d)\n",bit,fwd_bit);
      }
    }
  }
  else{
    for(bit=2; bit<=prime_size; bit++){
      if(TestBit(primes,bit) == 0){
	if(TestBit(primes,bit+2) == 0){
	  num_twin_primes++;
	}
      }
    }
  }
  
  free(primes);

  exit(EXIT_SUCCESS);
}
